package com.hfi.ood.atmsimulator;

import com.hfi.ood.atmsimulator.model.*;
import com.hfi.ood.atmsimulator.transaction.Transaction;
import lombok.Getter;
import lombok.Setter;
import org.joda.money.Money;

/**
 * @author ChangLiang
 * @date 2020/5/21
 */
public class Atm {

    private CardReader cardReader;

    private Display display;

    private Keyboard keyboard;

    @Getter
    private BankProxy bankProxy;

    private CashDispenser cashDispenser;

    private DepositSlot depositSlot;

    private Printer printer;

    /**
     * cardNo and password is valid
     */
    private boolean isValid;

    @Getter
    @Setter
    private Money holdMoney;

    public Atm() {
        cardReader = new CardReader();
        display = new Display();
        // attach
        cardReader.attach(display);
        keyboard = new Keyboard();
        bankProxy = new BankProxy();
        cashDispenser = new CashDispenser();
        depositSlot = new DepositSlot();
        printer = new Printer();
    }

    public void printOptions(){
        display("\n--- To Do Application ---");
        display("Please make a choice:");
        display("(i)--insert a card");
        display("(w)--withdraw");
        display("(d)--deposit");
        display("(q)--query balance");
        display("(t)--transfer");
        display("(e)--eject a card");
        display("(s)--shutdown machine");
    }

    public void processInput(CommandLineInput input) {
        if (input == null) {
            handleUnknownInput();
        } else {
            switch (input){
                case INSERT_CARD:
                    display("please input card no");
                    String cardNo = readInput();
                    cardReader.readCardInfo(cardNo);
                    isValid = validateCardNoAndPassword(cardReader.getCardNo());
                    break;
                case WITHDRAW:
                case DEPOSIT:
                case QUERY_BALANCE:
                case TRANSFER:
                    if (isValid) {
                        if (doTransaction(input)) {
                            break;
                        }
                    }
                    handleInvalidInput();
                    break;
                case EJECT_CARD:
                    cardReader.ejectCard();
                    break;
                case EXIT:
                    break;
                default:
                    handleUnknownInput();
            }
        }
    }

    public static final int ALLOWED_FAILURE_TIMES = 3;

    public boolean validateCardNoAndPassword(String cardNo) {
        int failedCount = 0;
        boolean verified = false;

        while (failedCount< ALLOWED_FAILURE_TIMES) {
            // input password
            String password = keyboard.readPassword(this);
            verified = bankProxy.validateCardNoAndPassword(cardNo, password);
            if (verified) {
                break;
            }

            failedCount++;
            if (failedCount == 3) {
                break;
            }
            display("please input password again, only allow " + (ALLOWED_FAILURE_TIMES-failedCount) + " times");
        }
        if (!verified) {
            cardReader.eatCard();
        }
        return verified;
    }

    private boolean doTransaction(CommandLineInput input) {
        Transaction transaction = TransactionManager.createTransaction(input.getShortCmd());
        if (transaction.preProcess(this)) {
            transaction.doTransaction(this);
            transaction.postProcess(this);
            printer.addTransaction(transaction);
            return true;
        }
        return false;
    }

    public void print() {
        printer.print();
    }

    public String getCardNo() {
        return this.cardReader.getCardNo();
    }

    public String doDeposit(Money money) {
        return depositSlot.doDeposit(money);
    }

    public String dispenseCash(Money money) {
        return cashDispenser.dispenseCash(money);
    }

    public String readInput() {
        return keyboard.readInput();
    }

    public void display(String str) {
        display.display(str);
    }

    private void handleUnknownInput() {
        display("Please select a valid option!");
    }

    private void handleInvalidInput() {
        display("Please insert card and input correct password");
    }
}
